<?php

namespace App\Workerman;

use App\Http\Controllers\Auth\JwtToken;
use App\Http\Controllers\Chat\ChatGroupController;
use Workerman\Worker;
use GatewayWorker\Lib\Gateway;
use Workerman\Lib\Timer;
use Workerman\Connection\TcpConnection;
use Illuminate\Support\Facades\Log;
use Workerman\Mqtt\Client;

/**
 * @group mqtt协议接口消息处理
 *
 * 采用 restful api
 *
 * Body Parameters 表示请求的所需要的参数
 *
 * Query Parameters 表示返回参数的名称
 */
class Events
{
    static $connection_count = 0;
    static $groupIdBase = 10000; //比如群消息的id
    static $employeeBase = 20000;

    public static function onWorkerStart($businessWorker)
    {
        global $db;
        $db = new \Workerman\MySQL\Connection('server.southmeteor.com', '3306', 'zhihuihuanwei', 'zPsNnM3mW8jBdHcR', 'zhihuihuanwei');
    }

    public static function onConnect($client_id)
    {
        Gateway::sendToClient($client_id, json_encode(array('type' => 'init', 'msg' => 200)));
    }

    function handle_connection($connection)
    {
    }

    public static function onWebSocketConnect($client_id)
    {

        Gateway::sendToClient($client_id, json_encode(array('type' => 'conn', 'msg' => 200)));
    }

    /**
     * @param $client_id
     * @param $message
     *
     * 消息格式 dict 传递消息使用json封装
     *  各字段含义
     *
     * 172.16.95.128:8000/api/chat/foo?token&type=login&msg=null 消息测试接口
     * 172.16.95.128:8000/api/chat/login
     *
     *  token: string类型|null (令牌信息,通过员工登录api获取 类型:string 该字段类似session 在login模式下使用来建立链接，建立后传递null)
     *  type: msg|ack|conn|login|err|init （消息类型 0->init初始化链接成功 1. msg->客户端或服务器向对方发送消息 2. ack 确认消息已经收到 3. conn确认链接成功或断开成功 4. login 在链接成功之后第一步是发送token 建立session 5. err消息错误）
     *  msg: string类型|null (消息内容 1. 如果有消息就发送json类型信息 2.如果是ack等确认无消息就发送null)
     *  msg_type: group|employee|broadcast|null (发送消息的类型 group是表示向群里面发消息,employee表示个人间聊天,broadcast表示广播给所有人的消息,其他时候比如login没有消息传递的时候使用null即可)
     *  msg_pk: int类型|null (消息传递的对象id号,如果msg_type是group那么传递群聊的聊天号从/api/chat/groups中获取(提交第一层的id),如果是employee那么传递employee的id号,从/api/chat/groups中获取(users->id)
     *
     * 前端相关事项:
     *
     *  1. 通过前端ws协议链接到后端(可以使用该网址测试后端ws是否运行,弱后端ws无服务请及时通知后端处理,其他前端或app等ws的链接等问题请自行解决。)
     *
     *
     * 各场景使用流程
     *
     *
     *
     *
     * 1. 建立链接:
     *     1. 通过前端ws协议链接到后端(可以使用该网址测试后端ws是否运行,弱后端ws无服务请及时通知后端处理,其他前端或app等ws的链接等问题请自行解决。)
     *     2. 发送登录接口获取到的token 消息格式 type->login,msg->null,token->(获取到的token)来登录ws
     *     3. 请求信息:
     *          无需任何信息,遵照websocket协议链接即可
     *     3. 反馈信息:
     *
     *         1. type->conn
     *         2. msg: 200->链接成功 407->消息格式错误  401->token过期 500:服务器错误,尝试重连
     * 2. 链接成功后立刻登录:
     *
     *    前端逻辑部分
     *
     *    1. 发送登录接口获取到的token 消息格式 type->login,msg->null,token->(获取到的token)来登录ws
     *    2. 请求信息:
     *         1. token: 用户登录接口的token /api/user/login
     *         2. type: login
     *         3. msg: null
     *    3. 返回信息:
     *         1. token: null
     *         2. type: ack
     *         3. msg: 200->登录成功 401->token失效 无法认证用户 500:服务器错误,尝试重连
     *
     *    后端逻辑部分
     *
     *    1. 把用户id和client_id进行绑定
     *    2. 读取出用户所属的组织
     *    3. 根据用户的组织把用户加入到组织名映射的group中去
     *
     */
    public static function onMessage($client_id, $message)
    {
        $msg = self::verifyMsg($message);

        if (!$msg) {
            Gateway::sendToClient($client_id, json_encode(array('type' => 'err', 'msg' => 407)));
            return;
        }
        self::handler($client_id, $message);
    }

    //客户端与Gateway进程的连接断开时触发。
    public static function onClose($client_id)
    {
        Log::info('close connection' . $client_id);
    }

    public static function onWorkerStop($businessWorker)
    {
        echo "WorkerStop\n";
    }

    public static function encodeData($type = 'ack', $msg = 'null', $token = 'null', $to = 'group', $file = null)
    {
        $data = array('type' => $type, 'msg' => self::encodeMsg($msg, $token, $to, $file));
        return $data;
    }


    public static function verifyMsg($msg)
    {
        try {
            $data = (array)json_decode($msg);
        } catch (\Exception $e) {
            return false;
        }
        if ($data === null) {
            return false;
        }
        if (sizeof($data) == 0) {
            return false;
        }
        if (!array_key_exists('type', $data) || !array_key_exists('token', $data) || !array_key_exists('msg', $data)) {
            return false;
        }

        if (!$data['msg'] === 'null') {
            if (!array_key_exists('msg_type', $data) || !array_key_exists('msy_pk', $data)) {
                return false;
            }
        }
        return $data;
    }


    public static function handler($client_id, $msg)
    {

        $senderUid = Gateway::getUidByClientId($client_id);
        $senderUid = $senderUid - self::$employeeBase;
        $msg = (array)json_decode($msg);
        $token = $msg['token'];
        $employee = JwtToken::verifyToken($token);
        if (!$employee) {
            Gateway::sendToClient($client_id, json_encode(array('type' => 'err', 'msg' => 401)));
            return;
        }
        $employeeId = $employee['id'];
        if ($msg['type'] == 'login') {
            $uid = Gateway::getUidByClientId($client_id);
            if ($uid) {
                self::sendMsg($uid - self::$employeeBase, self::encodeData('login', 200, $token, 'employee'));
                return;
            }
            global $db;
            $employeeChat = $db->query(ChatGroupController::getSql('employee', $employeeId));
            $DepartmentChat = $db->query(ChatGroupController::getSql('department', $employeeId));
            $ids = array_merge($employeeChat, $DepartmentChat);
            $ids = array_map(function ($item) {
                return $item['chat_group_id'];
            }, $ids);
            $ids = array_unique($ids);
            foreach ($ids as $id) {
                Gateway::joinGroup($client_id, self::chatGroupIdToWSGroupId($id));
            }

            Gateway::bindUid($client_id, self::chatEmployeeIdToWSEmployeeId($employeeId));
            self::sendMsg($employeeId, self::encodeData('login', 200, $token, 'employee'));
        } //群发消息逻辑
        elseif ($msg['type'] == 'msg') {
            $type = $msg['msg_type'];
            $pk = $msg['msg_pk'];
            $file = array_key_exists('file', $msg) ? $msg['file'] : null;
            if ($type === 'group') {
                $returnMsg = self::encodeData('msg', $msg['msg'], $token, 'group', $file);
                self::sendMsg($pk, $returnMsg, true, $client_id);
            } elseif ($type === 'employee') {
                //把消息发送给个人
                $returnMsg = self::encodeData('msg', $msg['msg'], $token, 'employee', $file);
                self::sendMsg($pk, $returnMsg, true, $client_id);
                self::sendMsg($senderUid, $returnMsg);
            }
        } else {
            $returnMsg = self::encodeData('err', 'message no valid', $token, 'employee');
            self::sendMsg($senderUid, $returnMsg);
        }
    }

    public static function chatGroupIdToWSGroupId(int $chatId)
    {
        return $chatId + self::$groupIdBase;
    }

    //数据库中的聊天群id 和workerman中的groupid进行绑定,前段发送消息过来只用传递聊天群id即可
    //传递聊天群的数据库主键过来 转换为聊天群id号
    public static function chatEmployeeIdToWSEmployeeId(int $chatId)
    {
        return $chatId + self::$employeeBase;
    }

    //查找用户所在的所有聊天群
    public static function userChatGroups(int $userId)
    {
        //查找用户的部门 筛选条件 这些部门要在表 chat_employ_organization 中
    }

    public static function encodeMsg($msg, $token, $to = 'group', $file = null)
    {
        $employee = JwtToken::verifyToken($token);
        $data = ['file' => $file, 'msg' => $msg, 'time' => time(), 'name' => $employee['name'], 'id' => $employee['id'], 'avatar' => $employee['avatar'], 'to' => $to];
        return $data;
    }

    public static function sendMsg($pk, $msg, $record = false, $clientId = 'null')
    {
        global $db;
        $to = $msg['msg']['to'];
        $sender_id = $msg['msg']['id'];
        $message = $msg['msg']['msg'];
        $file = $msg['msg']['file'];
        $receiver = $pk;
        $msg['sender_id'] = $sender_id;
        $msg['receiver_id'] = $receiver;
        if ($to == 'group') {
            if ($record) {
                try {
                    $insert = array(
                        'chat_id' => $receiver,
                        'sender_id' => $sender_id,
                        'message' => $message,
                        'created_at' => date('Y-m-d H:i:s', time()),
                        'updated_at' => date('Y-m-d H:i:s', time()),
                    );
                    if (!($file == null))
                        $insert['file_id'] = $file;
                    $db->insert('hw_chat_group_message')->cols(
                        $insert
                    )->query();

                    $db->update('hw_chat_group')->cols(array('updated_at'))->where("id={$receiver}")->bindValue('updated_at',date('Y-m-d H:i:s', time()))->query();

                } catch (\Exception $e) {
                    Gateway::sendToClient($clientId, json_encode(array('type' => 'err', 'msg' =>'没该聊天组')));
                    return;
                }
            }
            Gateway::sendToGroup(self::chatGroupIdToWSGroupId($pk), json_encode($msg));
        } elseif ($to == 'employee') {
            if ($record) {
                try {
                    $date = date('Y-m-d H:i:s', time());
                    $insert = array(
                        'receiver_id' => $receiver,
                        'sender_id' => $sender_id,
                        'message' => $message,
                        'created_at' => $date,
                        'updated_at' => $date,
                    );
                    if (!($file == null))
                        $insert['file_id'] = $file;
                    $db->insert('hw_chat_employee_message')->cols(
                        $insert
                    )->query();

                    $res = $db->select('*')->from('hw_chat_users')->where("sender={$sender_id}")->where("receiver={$receiver}")->row();
                    if($res){
                        $db->update('hw_chat_users')->cols(array('updated_at'))->where("sender={$sender_id}")
                            ->where("receiver={$receiver}")->bindValue('updated_at',$date)->query();
                    }else{
                        $db->insert('hw_chat_users')->cols(
                            array(
                                "receiver"=>$receiver,
                                "sender"=>$sender_id,
                                "created_at"=>$date,
                                "updated_at"=>$date
                            )
                        )->query();
                    }
                } catch (\Exception $e) {
                    Gateway::sendToClient($clientId, json_encode(array('type' => 'err', 'msg' => 'no this employee')));
                    return;
                }
            }
            Gateway::sendToUid(self::chatEmployeeIdToWSEmployeeId($pk), json_encode($msg));
        }
    }
}